package productionPlan

import (
	"context"
	"errors"
	"github.com/google/uuid"
	"github.com/kuangshp/go-utils/k"
	"github.com/shopspring/decimal"
	"github.com/zeromicro/go-zero/core/logx"
	"golang.org/x/sync/errgroup"
	"gorm.io/gorm"
	"insighttracking/apps/admin/auth"
	"insighttracking/apps/admin/internal/svc"
	"insighttracking/apps/admin/internal/types"
	"insighttracking/common/enum"
	types2 "insighttracking/common/types"
	"insighttracking/common/utils"
	"insighttracking/query/bo"
	"insighttracking/query/dao"
	"insighttracking/query/model"
	"insighttracking/query/service"
	"net/http"
	"os"
	"strconv"
)

type ApiLogic struct {
	logx.Logger
	ctx     context.Context
	svcCtx  *svc.ServiceContext
	Request *http.Request
}

func NewApiLogic(ctx context.Context, svcCtx *svc.ServiceContext, request *http.Request) *ApiLogic {
	hostname, _ := os.Hostname()
	return &ApiLogic{
		Logger: logx.WithContext(ctx).WithCallerSkip(0).WithFields(
			logx.Field("Log_UUID", uuid.New().String()),
			logx.Field("hostname", hostname)),
		ctx:     ctx,
		svcCtx:  svcCtx,
		Request: request,
	}
}

func (l *ApiLogic) CreateApi(req *types.CreateProductionPlanReq) (resp string, err error) {
	accountInfo, err := auth.GetAccountInfo(l.svcCtx, l.Request)
	if err != nil {
		return "", err
	}

	orderEntities, err := service.OrdersService.GetByIdListApi(l.ctx, accountInfo.EnterpriseId, req.OrderIds)
	if (err != nil && errors.Is(err, gorm.ErrRecordNotFound)) || len(orderEntities) == 0 {
		return "", errors.New("订单号有误，不是自己所属企业订单")
	}

	//根据OrderIdList拿到orderDetailsList
	totalMap, err := service.OrdersDetailService.GetTotalSpuApi(l.ctx, req.OrderIds)
	if err != nil {
		return
	}

	spuIDs := []int64{}
	for id, _ := range totalMap {
		spuIDs = append(spuIDs, id)
	}
	spuEntities, err := service.SpuService.GetSpuByIdListApi(l.ctx, spuIDs)
	if err != nil {
		return
	}
	spuTypeMap := make(map[int64]int64)
	for _, spu := range spuEntities {
		spuTypeMap[spu.ID] = spu.SpuType
	}

	tx := dao.Use(l.svcCtx.MysqlDb).Begin()
	var id int64
	produceDate := utils.DateStrToTime(req.ProduceDate)
	if id, err = service.ProductionPlanService.CreateApi(l.ctx, tx, &model.ProductionPlanEntity{
		ProduceDate:  produceDate,
		Name:         req.ProduceDate + "生产计划",
		EnterpriseID: accountInfo.EnterpriseId,
		Code:         utils.GenerateSN(l.svcCtx.RedisDb, produceDate, "SC"),
	}); err != nil {
		_ = tx.Rollback()
		return "", errors.New(err.Error())
	}

	g, ctx := errgroup.WithContext(l.ctx)
	//构造生产计划详情
	details := make([]*model.ProductionPlanDetailEntity, 0)
	for _, item := range totalMap {
		if _, ok := spuTypeMap[item.SpuID]; ok {
			details = append(details, &model.ProductionPlanDetailEntity{
				ProductionPlanID: id,
				SpuID:            item.SpuID,
				Weight:           item.Count,
				Unit:             item.UnitName,
			})
		}
	}

	g.Go(func() (err error) {
		err = service.ProductionPlanService.CreateDetailApi(ctx, tx, details)
		if err != nil {
			err = tx.Rollback()
		}
		return
	})

	//更新订单状态
	g.Go(func() (err error) {
		err = service.OrdersDetailService.UpdateProduceStatusApi(ctx, tx, req.OrderIds, 1)
		if err != nil {
			err = tx.Rollback()
		}
		return
	})

	g.Go(func() (err error) {
		err = service.OrdersService.UpdateProduceStatusApi(ctx, tx, req.OrderIds, 1)
		if err != nil {
			err = tx.Rollback()
		}
		return
	})

	//绑定生产计划&订单关系
	productionOrdersEntities := make([]*model.ProductionOrdersEntity, 0)
	for _, orderId := range req.OrderIds {
		productionOrdersEntities = append(productionOrdersEntities, &model.ProductionOrdersEntity{
			ProductionPlanID: id,
			OrdersID:         orderId,
		})
	}
	g.Go(func() (err error) {
		err = service.ProductionPlanService.CreatePlanOrderRelationApi(ctx, tx, productionOrdersEntities)
		if err != nil {
			err = tx.Rollback()
		}
		return
	})

	err = g.Wait()
	if err != nil {
		_ = tx.Rollback()
		return "", err
	}
	if err = tx.Commit(); err != nil {
		return "", errors.New("创建失败")
	}
	return "操作成功", nil
}

func (l *ApiLogic) GetListApi(req *types.ProductionPlanListReq) (resp []types2.SimpleList, err error) {
	accountInfo, err := auth.GetAccountInfo(l.svcCtx, l.Request)
	if err != nil {
		return make([]types2.SimpleList, 0), nil
	}
	productionList, err := service.ProductionPlanService.GetListApi(l.ctx, accountInfo.EnterpriseId, req.BeginDate, req.EndDate)
	if err != nil {
		return make([]types2.SimpleList, 0), nil
	}
	return k.Map(productionList, func(item *model.ProductionPlanEntity, index int) types2.SimpleList {
		return types2.SimpleList{
			ID:   item.ID,   // id
			Name: item.Name, // 名称
			Code: item.Code, // 编码
		}
	}), nil
}

func (l *ApiLogic) GetDetailListApi(req *types.IdReq) (resp []types.ProductionPlanDetailListResp, err error) {
	resp = make([]types.ProductionPlanDetailListResp, 0)
	productionPlanDetails, err := service.ProductionPlanService.GetDetailListApi(l.ctx, req.Id)
	resp = k.Map(productionPlanDetails, func(item *bo.ProductionPlanDetailBo, index int) types.ProductionPlanDetailListResp {
		weight, _ := item.Weight.Float64()
		return types.ProductionPlanDetailListResp{
			ID:        item.ID,
			SpuName:   item.SpuName,
			SpuID:     item.SpuID,
			Weight:    weight,
			Status:    item.Status,
			Unit:      item.Unit,
			CreatedAt: utils.TimestampToString(item.CreatedAt),
		}
	})
	return
}

func (l *ApiLogic) ModifyStatusByIdApi(req *types.ProductionPlanDetailStatusReq) (resp string, err error) {
	accountInfo, err := auth.GetAccountInfo(l.svcCtx, l.Request)
	if err != nil {
		return
	}

	// 任务检测，存在已下达的任务
	details, err := service.ProductionPlanService.GetDetailListByIDs(l.ctx, req.Ids)
	if err != nil {
		return "", err
	}

	detailsMap := make(map[int64]*bo.ProductionPlanDetailBo)
	for _, detail := range details {
		if detail.Status == enum.ProductionStatusLoading && req.Status == enum.ProductionStatusLoading {
			return "", errors.New("存在已下达的任务，请刷新页面重新选择")
		}
		if detail.Status == enum.ProductionStatusCollected || req.Status == enum.ProductionStatusCanceled {
			return "", errors.New("存在已领料的任务，无法取消，请刷新页面重新选择")
		}
		detailsMap[detail.ID] = detail
	}

	// 下达任务
	if req.Status == enum.ProductionStatusLoading {
		var productionPlanID int64
		productionPlanID = details[0].ProductionPlanID
		productionPlan, err := service.ProductionPlanService.GetInfoByIdApi(l.ctx, productionPlanID)
		if err != nil {
			return "", err
		}

		entities := make([]*model.PickingTaskEntity, 0)
		for _, id := range req.Ids {
			entity := &model.PickingTaskEntity{
				EnterpriseID:           accountInfo.EnterpriseId,
				ProductionPlanDetailID: id,
				SpuID:                  detailsMap[id].SpuID,
				SpuName:                detailsMap[id].SpuName,
				OrgSpuID:               detailsMap[id].OrgSpuID,
				OrgSpuName:             detailsMap[id].OrgSpuName,
				LossRate:               detailsMap[id].LossRate,
				Assigner:               strconv.Itoa(int(detailsMap[id].OperatorID)),
				Weight:                 detailsMap[id].Weight,
				ProduceDate:            productionPlan.ProduceDate,
			}
			entities = append(entities, entity)
		}
		err = service.ProductionPlanService.PublishTaskApi(l.ctx, entities)
	}

	if req.Status == enum.ProductionStatusCanceled {
		err = service.ProductionPlanService.RemoveTaskApi(l.ctx, accountInfo.EnterpriseId, req.Ids)
		if err != nil {
			return "", err
		}
	}
	if err = service.ProductionPlanService.ModifyStatusByIdsApi(l.ctx, req.Ids, req.Status); err != nil {
		return "", err
	}
	return "操作成功", nil
}

func (l *ApiLogic) GetOrdersApi(req *types.ProductionPlanOrdersReq) (resp *types.ProductionPlanOrdersResp, err error) {
	resp = &types.ProductionPlanOrdersResp{}
	accountInfo, err := auth.GetAccountInfo(l.svcCtx, l.Request)
	if err != nil {
		return
	}

	var orderIds []int64
	var productionPlan *model.ProductionPlanEntity
	//获取生产计划信息
	productionPlan, err = service.ProductionPlanService.GetInfoByIdApi(l.ctx, req.ProductionPlanID)
	//获取相关关联订单ID
	orderIds, err = service.ProductionPlanService.GetPlanOrdersApi(l.ctx, req.ProductionPlanID)

	//获取包含spu_id的订单
	var spuName string
	if req.SpuID != 0 {
		spuName, err = service.SpuService.GetNameByIdApi(l.ctx, req.SpuID)
		ordersDetailEntities, err := service.OrdersDetailService.GetOrderDetailBySpuIdApi(l.ctx, req.SpuID, orderIds)
		if err != nil {
			return resp, err
		}
		orderIds = []int64{}
		for _, entity := range ordersDetailEntities {
			orderIds = append(orderIds, entity.OrdersID)
		}
	}
	ordersEntities, err := service.OrdersService.GetByIdListApi(l.ctx, accountInfo.EnterpriseId, orderIds)
	resp.Name = productionPlan.Name
	resp.ProduceDate = utils.TimestampToString(productionPlan.ProduceDate)
	resp.SpuName = spuName
	orders := make([]types.PlanOrders, 0)
	for _, entity := range ordersEntities {
		customerName, _ := service.EnterpriseCustomerService.GetNameByIdApi(l.ctx, entity.CustomerID)
		orders = append(orders, types.PlanOrders{
			CreatedAt:     utils.TimestampToString(entity.CreatedAt),
			CustomerName:  customerName,
			ID:            entity.ID,
			OrdersNo:      entity.OrderNo,
			SendGoodsDate: utils.TimestampToString(entity.SendGoodsDate),
		})
	}
	resp.Total = int64(len(orders))
	resp.PageSize = 1
	resp.PageNumber = int64(len(orders))
	resp.Orders = orders
	return
}

func (l *ApiLogic) GetInOutDetailApi(req *types.GetProductionPlanInOutDetailReq) (resp *types.GetProductionPlanInOutDetailResp, err error) {
	resp = &types.GetProductionPlanInOutDetailResp{}
	list := make([]*types.ProductionPlanInOutDetailList, 0)
	resp.List = list

	accountInfo, err := auth.GetAccountInfo(l.svcCtx, l.Request)
	if err != nil {
		return
	}

	inOutDetails, count, err := service.ProductionPlanService.GetInOutDetailApi(l.ctx, accountInfo.EnterpriseId, req.ProductionPlanID, req.SpuID, req.PageSize, req.PageNumber)
	if err != nil {
		return
	}

	for _, entity := range inOutDetails {
		storeSortingDetail, err := service.StoreSortingDetailService.GetStoreSortingDetailByProductionPlanApi(l.ctx, entity.ProductionPlanDetailID, entity.ProcessSpuID)
		if err != nil {
			return resp, err
		}
		outCount := decimal.New(0, 0)
		for _, v := range storeSortingDetail {
			outCount.Add(v.OutboundCount)
		}

		inCount, err := service.PickingService.GetPickingCount(l.ctx, entity.ProductionPlanDetailID, entity.BaseSpuID)
		if err != nil {
			return resp, err
		}
		list = append(list, &types.ProductionPlanInOutDetailList{
			BaseSpuCode:        entity.BaseSpuCode, // 原材料ID
			BaseSpuID:          entity.BaseSpuID,   // 原材料名称
			BaseSpuName:        entity.BaseSpuName, // 投入
			InCount:            inCount,            // 损耗率（数字，前端自行补百分号）
			LossRate:           inCount.Sub(outCount).Div(inCount),
			LossRateInitial:    inCount.Sub(outCount).Div(inCount), // 相对初始损耗率（数字，前端自行补百分号）
			OutCount:           outCount,                           // 产出
			ProcessSpuBOMCode:  "",                                 // 成品编码
			ProcessSpuID:       entity.ProcessSpuID,                // 成品ID
			ProcessSpuName:     entity.ProcessSpuName,              // 成名名称
			ProductionLines:    entity.ProductionLines,             // 生产线
			ProductionPlanID:   entity.ProductionPlanID,            // 生产计划ID
			ProductionPlanName: entity.ProductionPlanName,          // 生产计划名称
		})
	}

	resp.Total = count
	resp.PageSize = req.PageSize
	resp.PageNumber = req.PageNumber
	resp.List = list
	return
}
